/////////////////////////////////////////////////////////////////////////////
//
// (c) 2007 BinaryComponents Ltd.  All Rights Reserved.
//
// http://www.binarycomponents.com/
//
/////////////////////////////////////////////////////////////////////////////

using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using SuperListEx.ItemLists;
using SuperListEx.Sections;
using SuperListEx.WinFormsUtility.Controls;
using SuperListEx.WinFormsUtility.Drawing;
using System.Collections.Generic;


namespace SuperListEx
{
  public class ListControl : SectionContainerControl
  {
    #region vars
    private int test = 0;
    private Section _customiseListSection;
    private bool _allowSorting = true;
    List<Keys> _interestedKeys = new List<Keys>() { Keys.Space, Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Multiply, Keys.Subtract };
    #endregion

    #region Events
    public event EventHandler CalculatedGroupRows;
    public event EventHandler<RowEventArgs> MouseClickedRow;
    public event EventHandler<CellEventArgs> MouseClickedCell;
    public event EventHandler<RowFocusChangedEventArgs> RowFocusChanged;
    public event EventHandler RowDoubleClicked;
    #endregion

    #region Delegates
    [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
    public CustomColorDelegate CustomBackgroundColorAccessor { get; set; }

    [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
    public CustomColorDelegate CustomForegroundColorAccessor { get; set; }

    [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
    public CustomFontDelegate CustomFontAccessor { get; set; }
    #endregion

    #region Properties
    public bool MultiSelect
    {
      get { return ListSection.AllowMultiSelect; }
      set { ListSection.AllowMultiSelect = value; }
    }

    public bool ShowHeaderSection
    {
      get { return ListSection.ShowHeaderSection; }
      set { ListSection.ShowHeaderSection = value; }
    }

    public ListSection ListSection
    {
      get;
      private set;
    }

    public ColumnList Columns
    {
      get;
      private set;
    }

    public ItemList Items
    {
      get;
      private set;
    }

    public SelectedItemsCollection SelectedItems
    {
      get;
      private set;
    }

    public Color SeparatorColor
    {
      get;
      set;
    }

    public Color IndentColor
    {
      get;
      set;
    }

    public VerticalAlignment GroupSectionVerticalAlignment
    {
      get;
      set;
    }

    public Color AlternatingRowColor
    {
      get;
      set;
    }

    public Font GroupSectionFont
    {
      get;
      set;
    }

    public Color GroupSectionForeColor
    {
      get;
      set;
    }

    public string GroupSectionTextSingular
    {
      get;
      set;
    }

    public string GroupSectionTextPlural
    {
      get;
      set;
    }

    public int ItemCount
    {
      get
      {
        return Items.Count;
      }
    }

    public int SelectedItemCount
    {
      get
      {
        return GetSelectedItemList<object>().Count;
      }
    }

    public List<T> GetItemList<T>()
    {
      List<T> toReturn = new List<T>();
      Items.ToList().ForEach(I => toReturn.Add((T)I));
      return toReturn;
    }

    public List<T> GetSelectedItemList<T>()
    {
      List<T> toReturn = new List<T>();

      foreach (RowIdentifier ri in SelectedItems)
      {
        foreach (object o in ri.Items)
          toReturn.Add((T)o);
      }

      return toReturn;
    }

    public bool AllowSorting
    {
      get
      {
        return _allowSorting;
      }
      set
      {
        if (_allowSorting != value)
        {
          _allowSorting = value;
          foreach (Column clm in Columns)
          {
            clm.ShowHeaderSortArrow = _allowSorting;
          }
          LayoutControl();
        }
      }
    }

    public object FocusedItem
    {
      get
      {
        return ListSection.FocusedItem == null ? null : ListSection.FocusedItem.RowIdentifier.Items[0];
      }
      set
      {
        if (value != null)
        {
          PositionedRowIdentifier si = ListSection.GetPositionedIdentifierFromObject(new NonGroupRow(value));

          if (si != null)
          {
            int pos = si.Position - 1;

            while (pos >= 0)
            {
              RowIdentifier gi = ListSection.RowInformation[pos];

              if (gi is GroupIdentifier)
              {
                ListSection.FocusedItem = new PositionedRowIdentifier(gi, pos);

                --pos;
              }
              else
              {
                break;
              }
            }

            ListSection.FocusedItem = si;
          }
        }
        else
        {
          ListSection.FocusedItem = null;
        }
      }
    }

    public bool ShowCustomizeSection
    {
      get
      {
        return _customiseListSection != null;
      }
      set
      {
        if (ShowCustomizeSection != value)
        {
          if (_customiseListSection == null)
          {
            _customiseListSection = SectionFactory.CreateCustomiseListSection(this);
            Canvas.Children.Insert(0, _customiseListSection);
          }
          else
          {
            Canvas.Children.Remove(_customiseListSection);
            _customiseListSection.Dispose();
            _customiseListSection = null;
          }
          Canvas.Host.LazyLayout(Canvas);
        }
      }
    }

    public override SectionFactory SectionFactory
    {
      set
      {
        base.SectionFactory = value;

        Canvas.Children.Remove(_customiseListSection);
        Canvas.Children.Remove(ListSection);
        if (_customiseListSection != null)
        {
          _customiseListSection.Dispose();
        }
        bool headerShown = ListSection.ShowHeaderSection;
        ListSection.Dispose();

        if (ShowCustomizeSection)
        {
          _customiseListSection = SectionFactory.CreateCustomiseListSection(this);
          Canvas.Children.Add(_customiseListSection);
        }

        ListSection = SectionFactory.CreateListSection(this);
        ListSection.ShowHeaderSection = headerShown;
        Canvas.Children.Add(ListSection);
      }
    }

    public bool AllowRowDragDrop
    {
      get { return ListSection.AllowRowDragDrop; }
      set { ListSection.AllowRowDragDrop = value; }
    }
    #endregion

    #region Constuctors
    public ListControl(SectionFactory sectionFactory)
      : base(sectionFactory)
    {
      Columns = new ColumnList();
      Items = new BetterItemList();
      SelectedItems = new SelectedItemsCollection(this);
      KeyDown += new System.Windows.Forms.KeyEventHandler(ListControl_KeyDown);

      Name = "ListControl";
      GroupSectionTextSingular = "Item";
      GroupSectionTextPlural = "Items";

      GroupSectionVerticalAlignment = VerticalAlignment.Top;
      AlternatingRowColor = Color.Empty;
      GroupSectionForeColor = SystemColors.WindowText;
      Items.ListControl = this;
      _customiseListSection = SectionFactory.CreateCustomiseListSection(this);
      ListSection = SectionFactory.CreateListSection(this);
      Canvas.Children.Add(_customiseListSection);
      Canvas.Children.Add(ListSection);
      this.CustomFontAccessor = delegate(object o) { return SystemFonts.DialogFont; };
      this.CustomBackgroundColorAccessor = delegate(object o) { return this.BackColor; };
      this.CustomForegroundColorAccessor = delegate(object o) { return this.ForeColor; };
    }

    public ListControl()
      : this(new SectionFactory())
    {
      Columns.Parent = this;
    }
    #endregion

    #region Serilization
    public string SerializeState()
    {
      MemoryStream ms = new MemoryStream();
      StreamWriter writer = new StreamWriter(ms);
      writer.AutoFlush = true;
      SerializeState(writer);

      StreamReader reader = new StreamReader(new MemoryStream(ms.GetBuffer()));
      return reader.ReadToEnd();
    }

    public bool LoadState(string s)
    {
      try
      {
        if (s != "")
        {
          MemoryStream ms = new MemoryStream();
          StreamWriter writer = new StreamWriter(ms);
          writer.WriteLine(s);
          ms.Seek(0, SeekOrigin.Begin);
          this.DeSerializeState(new StreamReader(ms));
        }
        return true;
      }
      catch
      {
        return false;
      }
    }

    public virtual void SerializeState(TextWriter writer)
    {
      ListSection.SerializeState(writer);
    }

    public virtual void DeSerializeState(TextReader reader)
    {
      ListSection.DeSerializeState(reader);
    }
    #endregion

    #region Layout
    public void LayoutSections()
    {
      Canvas.Host.LazyLayout(Canvas);
    }

    /// <summary>
    /// Sets the column width to fit the specified columns contents.
    /// </summary>
    /// <param name="columns">Columns to size</param>
    public void SizeColumnsToFit(IEnumerable<Column> columns)
    {
      List<Column> c = new List<Column>(columns);
      ListSection.SizeColumnsToFit(c.ToArray());
    }
    #endregion

    #region Mouse
    protected virtual void OnMouseClickedRow(RowEventArgs eventArgs)
    {
      if (MouseClickedRow != null)
      {
        MouseClickedRow(this, eventArgs);
      }
    }

    protected virtual void OnRowDoubleClicked(RowSection rowSection)
    {
      if (RowDoubleClicked != null)
      {
        RowDoubleClicked(rowSection, EventArgs.Empty);
      }
    }

    protected virtual void OnMouseClickedCell(CellEventArgs eventArgs)
    {
      if (MouseClickedCell != null)
      {
        MouseClickedCell(this, eventArgs);
      }
    }

    public void PerformMouseWheel(int delta)
    {
      if (ListSection != null)
      {
        ListSection.MouseWheel(new MouseEventArgs(MouseButtons.None, 0, 0, 0, delta));
      }
    }

    protected override void OnMouseWheel(MouseEventArgs e)
    {
      base.OnMouseWheel(e);

      if (ListSection != null)
      {
        ListSection.MouseWheel(e);
      }
    }

    protected override void OnMouseClick(MouseEventArgs e)
    {
      base.OnMouseClick(e);

      Section section = SectionFromPoint(e.Location);
      while (section != null && !(section is RowSection))
      {
        section = section.Parent;
      }

      if (section != null)
      {
        RowSection rs = section as RowSection;
        OnMouseClickedRow(new RowEventArgs(rs.RowIdentifier));

        CellSection cs = rs.CellSectionFromPoint(new Point(e.X, e.Y));
        if (cs != null)
        {
          OnMouseClickedCell(new CellEventArgs(cs));
        }
      }
    }

    protected override void OnDoubleClick(EventArgs e)
    {
      base.OnDoubleClick(e);
      if (RowDoubleClicked != null)
      {
        Section section = SectionFromPoint(PointToClient(MousePosition));
        while (section != null)
        {
          if (section is RowSection)
          {
            OnRowDoubleClicked((RowSection)section);
            break;
          }
          section = section.Parent;
        }
      }
    }
    #endregion

    #region Keyboard
    protected override bool IsInputKey(Keys keyData)
    {
      return _interestedKeys.Contains(keyData);
    }

    private void ListControl_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.Control)
      {
        switch (e.KeyCode)
        {
          case Keys.C:
            if (SelectedItems.Count > 0)
            {
              StringBuilder strBldr = new StringBuilder();
              foreach (RowIdentifier row in SelectedItems)
              {
                if (row.Items.Count > 0)
                {
                  strBldr.AppendLine(row.Items[0].ToString());
                }
              }
              Clipboard.SetDataObject(strBldr.ToString());
            }
            break;
          case Keys.A:
            SelectedItems.Clear();
            SelectedItems.AddRange(Items.ToList());
            break;
        }
      }
    }
    #endregion

    #region Control Events
    protected virtual void OnRowFocusChanged(RowFocusChangedEventArgs eventArgs)
    {
      if (RowFocusChanged != null)
      {
        RowFocusChanged(this, eventArgs);
      }
    }

    protected internal virtual void OnCalculatedGroupRows(EventArgs e)
    {
      if (CalculatedGroupRows != null)
      {
        CalculatedGroupRows(this, e);
      }
    }
    #endregion

    #region Public functions
    public void EnsureVisible(object o)
    {
      if (o != null)
      {
        PositionedRowIdentifier si = ListSection.GetPositionedIdentifierFromObject(new NonGroupRow(o));

        if (si != null)
        {
          int pos = si.Position - 1;

          while (pos >= 0)
          {
            RowIdentifier gi = ListSection.RowInformation[pos];

            if (gi is GroupIdentifier)
            {
              ListSection.FocusedItem = new PositionedRowIdentifier(gi, pos);
              --pos;
            }
            else
            {
              break;
            }
          }

          ListSection.FocusedItem = si;
        }
      }
      else
      {
        ListSection.FocusedItem = null;
      }
    }

    public void SetGlobalGroupState(GroupState state)
    {
      ListSection.SetGlobalGroupState(state);
    }

    #region CSV
    public string ExportListToCSV(bool includeHeaders)
    {
      StringBuilder toReturn = new StringBuilder();
      if (includeHeaders)
        toReturn.Append(GenHeaders());

      foreach (object o in GetItemList<object>())
      {
        for(int i = 0; i<Columns.Count; i++)
        {
          Column c = Columns[i];
          if (i > 0) toReturn.Append(",");
          object iData = c.ColumnItemAccessor(o);
          string sData = iData == null ? "" : iData.ToString();
          toReturn.Append(CleanCSVString(sData));
        }
        toReturn.Append(Environment.NewLine);
      }
      return toReturn.ToString();
    }

    private string GenHeaders()
    {
      StringBuilder sb = new StringBuilder();
      foreach (Column c in Columns)
      {
        if (sb.Length > 0) sb.Append(",");
        sb.Append(CleanCSVString(c.Name));
      }

      sb.Append(Environment.NewLine);
      return sb.ToString();
    }

    private string CleanCSVString(string toClean)
    {
      string toReturn = toClean;
      if (toReturn == "\"")
        toReturn = "";

      if (toReturn.Contains(",") && (!toReturn.StartsWith("\"")))
      {
        toReturn = string.Format("\"{0}\"", toReturn);
      }

      if (toReturn.StartsWith("\"") && !toReturn.EndsWith("\""))
      {
        toReturn = string.Format("{0}\"", toReturn);
      }

      if (toReturn.EndsWith("\"") && !toReturn.StartsWith("\""))
      {
        toReturn = toReturn.Insert(0, "\"");
      }
      return toReturn;
    }
    #endregion
    #endregion

    internal void UpdateListSection(bool lazyLayout)
    {
      ListSection.ListUpdated(lazyLayout);
      ListSection.Invalidate();
    }

    internal void FireFocusChanged(RowIdentifier oldFocusItem, RowIdentifier newFocusItem)
    {
      OnRowFocusChanged(new RowFocusChangedEventArgs(oldFocusItem, newFocusItem));
    }
  }
}